﻿using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
using System.Threading;

namespace _1._2SimpleReceive
{
    /// <summary>
    /// 消息接收端
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            //1.实例化连接工厂
            var factory = new ConnectionFactory()
            {
                UserName = "admin",//用户名
                Password = "admin",//密码
                HostName = "192.168.0.248" //rabbitmq ip
            };
            //2. 建立连接
            using (var connection = factory.CreateConnection())
            {
                //3. 创建信道
                using (var channel = connection.CreateModel())
                {
                    //4. 声明队列
                    //4.1 直接从队列中获取消息
                    channel.QueueDeclare(queue: "hello", durable: true, exclusive: false, autoDelete: false, arguments: null);
                    
                    //***消息分发方式
                    //rabbitmq的QOS机制，
                    //这里简单解释一下，当生产者将消息发布到rabbitmq之后，
                    //如果在未配置QOS的情况下,rabbitmq尽可能快速地发送队列中的所有消息到消费者端
                    //如果消息比较多，消费者来不及处理，就会缓存这些消息，当消息堆积过多，可能导致服务器内存不足而影响其他进程
                    //rabbitmq的QOS可以很好的解决这类问题
                    //QOS就是限制消费者一次性从rabbitmq中获取消息的个数，而不是获取所有消息。
                    //比如设置rabbitmq的QOS为10，也就是prefetchCount = 10，
                    //就是说，哪怕rabbitmq中有100条消息，消费者也只是一次性获取10条，然后消费者消费这10条消息，剩下的交给其他消费者
                    //当10条消息中的unacked个数少于prefetch* 消费者数目时，会继续从rabbitmq获取消息
                    //如果在生产模式中，不使用QOS，你会发现，所有的消息都被一个消费者消费了
                    //##设置QOS
                    //设置消费者每次获取的消息数，可以用来设置消费者消息的权重
                    //必须等获取的消息都消费完成后才能重新获取
                    channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);

                    //5. 构造消费者:事件基本消费者
                    var consumer = new EventingBasicConsumer(channel);
                    //6. 绑定消息接收后的事件委托
                    consumer.Received += (model, ea) =>
                    {
                        var message = Encoding.UTF8.GetString(ea.Body.ToArray());
                        Console.WriteLine($"收到消息： {message}");      
                        
                        Thread.Sleep(500);//模拟耗时,进行消息处理                        
                        Console.WriteLine("消息处理结束\n");

                        // 7. 发送消息确认信号（手动消息确认）
                        channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
                    };

                    //RabbitMQ采用消息应答机制，即消费者收到一个消息之后，需要发送一个应答
                    //然后RabbitMQ才会将这个消息从队列中删除，
                    //如果消费者在消费过程中出现异常断开连接切没有发送应答，那么RabbitMQ会将这个消息重新投递。
                    //7. 启动消费者
                    //autoAck:true；自动进行消息确认，当消费端接收到消息后，就自动发送ack信号，不管消息是否正确处理完毕
                    //autoAck:false；关闭自动消息确认，通过调用BasicAck方法手动进行消息确认
                    channel.BasicConsume(queue: "hello", autoAck: false, consumer: consumer);

                    Console.WriteLine("消费者已启动");

                    //程序不结束，等待输入
                    Console.WriteLine(" 输入 [enter] 退出程序.\n");                    
                    Console.ReadLine();
                }
            }
        }
    }
}
